package be.nepherte.movmanager.client.ui.tabframework;

import java.util.ArrayList;

import be.nepherte.movmanager.client.ui.tabframework.Tab.TabInfo;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class represents a menu list of {@link Tab Tabs} and supports adding,
 * removing and finding tabs. This menu list starts with a MovManager logo,
 * followed by the tabs in this menu. Only 1 tab can be active at the time.
 * The active tab has the tab's native color, all inactive tabs have the
 * same background color. Each tab in the menu list contains a mouse over
 * effect. Whenever the mouse hovers over the tab, the tab gets its native
 * color.
 * 
 * <p>
 * Copyright © 2010 Bart Verhoeven
 * </p>
 * 
 * <p>
 * This file is part of MovManager.
 * </p>
 * 
 * <p>
 * MovManager is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * </p>
 * 
 * <p>
 * MovManager is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * </p>
 * 
 * <p>
 * You should have received a copy of the GNU General Public License along with
 * MovManager. If not, see http://www.gnu.org/licenses/.
 * </p>
 * 
 * @author	Bart Verhoeven
 * @version	0.1
 */
public class TabList extends Composite {
	/**The horizontal panel where all the tabs (Mouse links) are placed upon.*/
	private HorizontalPanel list = new HorizontalPanel();
	/** A list containing all tab info objects added to this tab list.*/
	private ArrayList<TabInfo> tabs = new ArrayList<TabInfo>();
	/**The index of the currently selected tab.*/
	private int selectedTab = -1;
	
	/**
     * Creates a new tab list with them MovManager logo and no tabs.
     * 
     * @param	images
     * 			image provider for tabs
     */
	public TabList(Tab.Images images) {
		initWidget(list);
		list.add(new Image(images.logo()));
		setStyleName("ks-List");
	}
	
	/**
     * Adds a tab to the list of tabs.
     * 
     * @param	info
     *          the info of the tab that should be added 
     */
	public void addTab(final TabInfo info) {
		String name = info.getName();
		int index = list.getWidgetCount() - 1; // the image logo doesn't count

		MouseLink link = new MouseLink(name, index);
		list.add(link);
		tabs.add(info);

		list.setCellVerticalAlignment(link, HorizontalPanel.ALIGN_BOTTOM);
		styleTab(index, false);
	}
	
	/**
     * Removes a tab from the list of tabs.
     * 
     * @param	info
     *          the info of the tab that should be removed
     */
	public void removeTab(final TabInfo info) {
		String name = info.getName();
		
		for (Widget w: list) {
			if (w instanceof MouseLink) {
				MouseLink link = (MouseLink) w;
			
				if (link.getText() == name) {
					tabs.remove(info);
					list.remove(link);
					return;
				}
			}
		}
	}
	
	/**
     * Sets a tab as selected. This includes setting the style of a selected tab. 
     * 
     * @param	name
     *         	the name of the tab to be selected
     */
	public void setTabSelection(String name) {
		// deselect current tab
		if (selectedTab != -1)
			styleTab(selectedTab, false);

		// select given tab
		for (int i = 0; i < tabs.size(); ++i) {
			TabInfo info = (TabInfo) tabs.get(i);
			if (info.getName().equals(name)) {
				selectedTab = i;
				styleTab(selectedTab, true);
				return;
			}
		}
		
		// the name of the tab to be selected was not found,
		// restore the previously selected tab
		if (selectedTab != -1)
			styleTab(selectedTab, false);
	}
	
	/**
	 * Finds a tab by its name. When this tab list doesn't have a tab with the
	 * given name, it will return null.
	 * 
	 * @param	tabName
	 *          the name of the tab you are looking for.
	 * @return	the tab info of the tab with the given name or null if not found
	 */
	public TabInfo find(String tabName) {
		for (TabInfo info: tabs) {
			if (info.getName().equals(tabName))
				return info;
		}

		return null;
	}
	
	/**
	 * Applies a style to a tab. The style applied is <tt>ks-FirstSinkItem</tt> or
	 * <tt>ks-SinkItem</tt> depending on whether the tab is the first item or not. 
	 * When the tab is selected, it gets either <tt>ks-FirstSinkItem-selected</tt>
	 * or <tt>ks-SinkItem-selected</tt>. These styles are defined in <tt>style.css</tt>.
	 * 
	 * @param	index
	 *          the index of the tab that should get the style
	 * @param	selected
	 *          whether the tab is selected or not.
	 */
	private void styleTab(int index, boolean selected) {
		String style = (index == 0) ? "ks-FirstSinkItem" : "ks-SinkItem";
		
		if (selected) 
			style += "-selected";

		Widget w = list.getWidget(index + 1); // factor in image logo
		w.setStyleName(style);
		colorTab(index, selected);
	}
	
	/**
	 * Colors a tab. If the tab is selected, it will get its color as specified
	 * in its TabInfo. If the tab is not selected, it will get the default light
	 * gray color.
	 * 
	 * @param	index
	 *          the index the tab that should be colored
	 * @param 	on
	 *          whether the tab is active or not
	 */
	private void colorTab(int index, boolean on) {
		String color = "";
		if (on)
			color = ((TabInfo) tabs.get(index)).getColor();

		Widget w = list.getWidget(index + 1); // factor in image logo
		DOM.setStyleAttribute(w.getElement(), "backgroundColor", color);
	}
	
	/**
     * Sets a tab visible.
     * 
     * @param	tab
     *          the info of the tab that should be made visible
     * @param   visible
     *          whether the tab should be visible or not.
     */
	public void setVisible(TabInfo tab, boolean visible) {
		for (int i = 0; i < tabs.size(); ++i) {
			TabInfo info = (TabInfo) tabs.get(i);
			if (info.equals(tab)) {
				Widget w = list.getWidget(i + 1);
				w.setVisible(visible);
				return;
			}
		}
	}
	
	
	/**
     * A hyper link class with a mouse over and mouse out effect.
     */
	private class MouseLink extends Hyperlink {
		/**The index of this instance*/
		private int index;

		/**
		 * The constructor for this class. Creates the hyper link with the mouse
		 * hover effect.
		 * 
		 * @param	name
		 *          name/text of the hyper link
		 * @param	index
		 *          the index of the tab associated with this mouse link
		 */
		public MouseLink(String name, int index) {
			super(name, name);
			this.index = index;
			sinkEvents(Event.MOUSEEVENTS);
		}
		
		/**
		 * A mouse out effect that changes the color of a tab when you remove
		 * the mouse over it. The color will either be the tab color as
		 * specified in TabInfo if it is a selected tab or the default light
		 * gray background for unselected tabs.
		 * 
		 * @param	index
		 *          the index of the tab that should get the mouse out effect
		 */
		private void mouseOut(int index) {
			if (index != selectedTab) 
				colorTab(index, false);
		}
		
		/**
		 * A mouse over effect that changes the color of a tab when you hover
		 * over it. The color will be the tab color as specified in TabInfo.
		 * 
		 * @param	index
		 *          the index of the tab that should get the mouse over effect
		 */
		private void mouseOver(int index) {
			if (index != selectedTab) 
				colorTab(index, true);
		}

		/**
		 * The actions to be performed on a browser event. Only 2 events are
		 * different from the normal on browser event of HyperLink: the mouse
		 * over and mouse out effect.
		 */
		@Override
		public void onBrowserEvent(Event event) {
			switch (DOM.eventGetType(event)) {
			case Event.ONMOUSEOVER:
				mouseOver(index);
				break;

			case Event.ONMOUSEOUT:
				mouseOut(index);
				break;
			}

			super.onBrowserEvent(event);
		}
	}
}
